import libtcodpy as libtcod
import bodysim, events, items
#======================== entity containers ============================
class BaseContainer(object):
	def __init__(self):
		self.objects = []
		
	def append(self,item):
		self.objects.append(item)
		
	def remove(self,item):
		if item in self.objects:
			self.objects.remove(item)
		else:
			print "ERROR: unable to remove item - not in container"	
		
#-----------------------------------------------------------------------
#NPC could not only 'store' items, they can also 'equip' them on themself
#-----------------------------------------------------------------------
class NPCContainer(BaseContainer):
	def __init__(self):
		super(NPCContainer,self).__init__()
		
		self.equipment = []
	
	def equip(self,item):
		if not item in self.equipment:
			
			#auto-unequip old item in same slot
			if item.slot:
				for old_item in self.equipment:
					if old_item.slot == item.slot:
						self.unequip(old_item)
			
				self.equipment.append(item)
			
			
		
	def unequip(self,item):
		if item in self.equipment:
			self.equipment.remove(item)
	
	def remove(self,item):
		self.unequip(item)
		super(NPCContainer,self).remove(item)

#======================= entity actions ================================
		
class EntAction(object):
	def __init__(self, owner, name):
		self.owner = owner
		self.name = name
		
	def act(self, caller = None):
		pass
		
class Entity(events.EventListener):
	def __init__(self, em, x, y, char, color):
		#super(Entity,self).__init__()
		
		self.x = x
		self.y = y
		self.model = char
		self.color = color
		
		self.em = em
		
		self.name = "none"
		self.obstacle = False
		
		#self.ai = None
		self.hp = 0
		self.bg_color = libtcod.BKGND_NONE
		
		self.container = None
		self.controller = None
		self.combat = None
		
		_map = em.services["map"]
		self.tile = _map.tiles[(x,y)]
		
	def spawn(self):
		self.em.post(events.EntSpawnEvent(self.x, self.y, self))
		
	def destroy(self):
		self.em.post(events.EntDestroyEvent(self))	
		
	def set_container(self,container):
		self.container = container
		
	def move(self, dx, dy):
		self.moveto(self.x+dx,self.y+dy)
			
	def moveto(self,x,y):
		map = self.em.map
		
		#if not (x > 0 and y > 0 and x < (map.w-1) and y < (map.h-1)):
		#	return
		
		if not map.is_blocked(x,y):
			from_x = self.x
			from_y = self.y
			
			self.x = x
			self.y = y
			
			self.em.post(events.EntMoveEvent(from_x, from_y, self.x,self.y, self))
			self.on_move(x,y)
			
	def on_move(self,x,y):
		pass
			
	def get_action_list(self):
		pass
		
	def render(self, x, y, con):
		#print "drawing object [" + str(self.x) + ","+ str(self.y) +"] at ["+str(x)+","+str(y)+"]";
		model = self.get_model()
		if not model:
			model = self.model
		
		libtcod.console_set_default_foreground(con, self.color)
		libtcod.console_set_default_background(con, self.bg_color)
		libtcod.console_put_char(con, x, y, model, libtcod.BKGND_NONE)
		
	#def clear(self):
	#	libtcod.console_put_char(self.em.con, self.x, self.y, ' ', libtcod.BKGND_NONE)
		
	def take_damage(self, damage):
		if isinstance(damage, bodysim.Damage):
			damage_amt = damage.dmg
		else:
			damage_amt = damage
			
		self.hp = int(self.hp) - damage_amt
		self.em.services["render"].message(self.name+" took "+str(damage_amt)+" damage",libtcod.magenta);
				
		if not self.is_alive():
			self.die()
				
	def is_alive(self):
		return self.hp > 0
		
	def is_obstacle(self):
		return self.obstacle
			
	def think(self):
		#do nothing
		pass
		
	def die(self):
		#Spawn every shit that is inside of me
		if self.container:
			for item in self.container.objects:
				item.drop_item(self)
				
		self.em.post(events.EntDieEvent(self))
		
	#added for shit like entity items	
	def touch(self):
		pass
		
	def describe(self, render):
		#render.
		pass
	
	def get_model(self):
		pass
		
	#~ def __str__(self):
		#~ msg = self.name
		#~ return msg
#TODO:
#ADD CONTAINERS THERE!!!!!!!!!!!!!
#LET Entity get it's own container like entity.container = BaseContainer		
#=======================================================================
class ItemEntity(Entity):
	def __init__(self, em, x, y, char, color):
		super(ItemEntity,self).__init__(em, x, y, char, color)
		self.obstacle = False
		self.ai = None
		self.body = None
		
		self.item = None
		
	def set_item(item):
		self.item = item
		
	def pick_up(self, picker):
		self.item.pick_up(picker)
		
		self.em.post(events.ConsoleMsg(picker.name + " picked up " + self.item.name))
		
	def take_damage(self, damage):
		#no damage to entity
		return 

#=======================================================================

class Actor(Entity):
	def __init__(self, em, x, y, char, color):
		super(Actor,self).__init__(em, x, y, char, color)
		self.obstacle = True
		
		self.ai = None
		self.body = None
		
		self.hp = 10
		
	def attack(self,x,y):
		#print "Mob is attacking @["+str(x)+","+str(y)+"]";
		
		map = self.em.services["map"]
		obj = map.get_obstacle(x,y)
		
		self.em.services["render"].message(self.name+" is attacking "+obj.name,libtcod.light_red);
		
		if self.combat:
			dmg_amt = self.combat.get_damage()
		else:
			dmg_amt = 10
			
		damage_type = "dmg_generic"
		if self.combat:
			damage_type = self.combat.get_damage_type()
			
		obj.take_damage(bodysim.Damage(dmg_amt,damage_type,self))
			
		
		#TODO: check if attack is violation of law
		#if so:
		self.em.post(
			events.CriminalAction(x,y,self)
		)
		if isinstance(obj,Human):
			#this part is requied by different kill effects
			#ie, if killer is player, killing should left trails on him (or other npc)
			self.em.post(
				events.MurderEvent(x, y, self, obj)	#self kills obj
			)
		
	#adding a bit of refactoring	
	def on_take_damage(self,damage):
		self.hp = int(self.hp) - damage.dmg
		self.em.services["render"].message(self.name+" took "+str(damage.dmg)+" damage",libtcod.magenta)
		
		if self.ai:
			self.ai.e_on_take_damage(damage.inflictor)

		if not self.is_alive():
			self.die(damage.inflictor)
			
	#def on_move(self,x,y):
		#map = self.em.services["map"]
			
	def take_damage(self, damage):
		if isinstance(damage, bodysim.Damage):
			print "Entity "+self.name+" is taking " + str(damage.dmg) + " damage of type '" + damage.type
	
			dmg_type = damage.type
			if self.body:
				self.body.take_damage(damage)
				
			#super(Actor,self).take_damage(damage.dmg)
			self.on_take_damage(damage)
			
			return
				
		#DEPRECATED DEPRECATED DEPRECATED
		else:
			super(Actor,self).take_damage(damage)
			if self.body:
				dmg = bodysim.Damage(damage)
				
				#print "Entity "+self.name+" is taking " + str(damage.dmg) + " damage of type '" + damage.type
				self.body.take_damage(dmg)
			else:
				print "Entity::take_damage() - no body simulation!"
			
		#pass
	
	def set_controller(self, controller):
		self.controller = controller
		if self.controller:
			self.controller.owner = self
			
	def set_combat(self, combat):
		self.combat = combat
		if self.combat:
			self.combat.owner = self
			self.combat.update_actor()	#quite important - set hp to max hp, etc
			
	def set_body(self, body):
		self.body = body
		if self.body:
			self.body.set_owner(self)
	
	def think(self):
		if self.is_alive() and self.controller:
			self.controller.think()
			
		if self.body:
			self.body.think()
		
	def die(self, killer = None):
		super(Actor,self).die()
		self.em.services["render"].message(self.name+" have died",libtcod.light_red);
		self.obstacle = False

		
	def notify(self,event):
		if self.controller:
			self.controller.notify(event)
			
	def moveto(self,x,y):
		if self.body:
			if not self.body.can_move():
				return
		super(Actor,self).moveto(x,y)
		
		
	#...................................................................
	def get_action_list(self):
		
		class RapeAction(EntAction):
			def act(self):
				#-------------HACK HACK HACK-------------
				caller = self.owner.em.services["player"]
				#----------------------------------------
				self.owner.em.services["render"].message(caller.name + " rapes "+self.owner.name);
				if self.owner.is_alive():
					self.owner.em.services["render"].message(self.owner.name + " screams like a pig");
					self.owner.take_damage(bodysim.Damage(5, "generic", caller))	#you can actualy rape victim to death
						
		class DismemberAction(EntAction):
			def act(self, caller = None):
				#-------------HACK HACK HACK-------------
				caller = self.owner.em.services["player"]
				#----------------------------------------
				
				limbs = self.owner.body.limbs
				
				limb = limbs[libtcod.random_get_int(0, 0,len(limbs)-1)]
				limbs.remove(limb)
				
				#create limb item and give it to player
				limb_item = items.Item(self.owner.name+"'s "+limb.name)
				limb_item.effects["damage"] = 1
				limb_item.effects["damage_type"] = "dmg_blunt"
				limb_item.slot = "weapon"
				limb_item.pick_up(caller)
				
				self.owner.em.services["render"].message(caller.name + " cuts off "+self.owner.name+"'s "+limb.name);
				
				#take much damage, scream in agony
				if self.owner.is_alive():
					self.owner.em.services["render"].message(self.owner.name+" screams in agony");
					#limb loss should ALLWAYS result in blodloss, so - dmg_cut
					self.owner.take_damage(bodysim.Damage(20*limb.dmg_multiply,"dmg_cut",caller))


		a_list = []
		if not self.body.can_move():
			a_list.append(RapeAction(self,"Rape"))
			a_list.append(DismemberAction(self,"Dismember"))
		
		
		return a_list
		
#---------------------base class for human. it can move, bleed, scream and die, and so on--------------		
class Human(Actor):
	def __init__(self, em, x, y, char, color):
		super(Human,self).__init__(em, x, y, char, color)
		self.sex = "male"	#showinistic pig
		self.age = "30"
		self.race = "white"
		self.religion = "atheist"
		
		self.apt = None
		
	def describe(self, render):
		render.message(self.name + " is "+ self.sex + ", age " + str(self.age))
		render.message(self.name + " is "+ self.race + ", " + self.religion)
		
		if not self.apt:
			render.message(self.name + " is homeless")
		#else:
		#	pass
			#todo: introduce occupation / apartments there?
		
		if self.combat:
			max_hp = self.combat.get_max_hp()
		else:
			max_hp = 10	#dummy
		
		if self.hp >= max_hp:
			render.message(self.name + " is healthy")
		elif self.hp >= int(max_hp * 0.5):
			render.message(self.name + " is injured")
		elif self.hp > 0:
			render.message(self.name + " is near death")
		elif self.hp <= 0:
			render.message(self.name + " is dead")
			
	def die(self, killer = None):
		super(Human,self).die(killer)
		#e = events.MurderEvent(killer,self)
		#---------------------------------------------------------------
		self.em.post(
			 events.SuspiciousSoundEvent(self.x, self.y, 10)
		)
		#---------------------------------------------------------------
		#~ self.em.post(
			 #~ events.MurderEvent(killer,self)
		#~ )
		#---------------------------------------------------------------
		
		#self.em.services["render"].message("MURDER POSTED",libtcod.red);
		#POST SUSPICIOUS SOUND MESSAGE	

	def get_model(self):
		if not self.is_alive():
			return "%"
			
		if self.body.stunned:
			return "?"
		
		if self.controller:
			__model = self.controller.get_model()
			if __model:
				return __model
				
		return "@"
		
